/*
--------------------------------------------------------------------------------
				Ak-Lib. 
			 Project       	: Ak-Lib
			 Group         	: Ak-Lib R&D Team
			 Author        	: Ocean.Wang
			 Create Date 	: 05-01-2010
--------------------------------------------------------------------------------
Code Name 	:  A.K 	~v~
Description	:  Yet Another Cross-Platform Lib.
Cautions    	:
--------------------------------------------------------------------------------
Change History:
Version		: 0.1
Building 		: 05/01/10
10-05.01 first draft, build, debug and test.
--------------------------------------------------------------------------------
*/

#ifndef _INC_AK_JOBQ_
#define	_INC_AK_JOBQ_

#include "ak_types.h"
#include "ak_queue.h"
#include "ak_lock.h"
#include "ak_mem.h"

#ifdef __cplusplus
extern "C" {
#endif

#define SJOBQ_MAGIC 		0XD2C3B4A5
#define SJOBQ_GAP 			4
#define SJOBQ_BAT_MAX		256


#define SJOB_PARAM_MAX		3
typedef struct {
	_t_sysMi 	mi;
	/* CB Entry */
	_t_cbentry 	cbEntry;
	void 	* 	pParam;
	long 		lParam[SJOB_PARAM_MAX];
}_t_sysJobNode;


#define SJOBQ_SLAB_MAX 		32 			/* 256 * (2^16) = 16M */
#define SJOBQ_SLAB_MINSZ 	256 		/* <= MinSz, We Use the Fixed Pool */

typedef struct {
	unsigned int 		magic;
	unsigned int		ringCnt;

	/* Rd/Wr Ix */
	unsigned int		rdIx;
	unsigned int		wrIx; 
	/* Ring Buf */
	_t_sysCS 			ringLock;
	_t_sysJobNode	**	ringJob;

	_t_sysSemB 		* 	wake;
	
	/* Job Pool:
	** 1. Simple Pool for Fixed Size Job.
	** 2. Adding the Slab Pool to Support Diff Large Message. 
	*/
	size_t 				slabs[SJOBQ_SLAB_MAX];
	_t_sysMSlot 		slots[SJOBQ_SLAB_MAX];
	_t_sysMPool 		mPool;
	_t_sysMHeap 		mHeap;
	
	/* Sche */
	unsigned int 		batch;
	unsigned int 		jStat;
	_t_cbentry 			jProc;
}_t_sysJobQ;

#define	SJOBQ_COUNT(pJobQ)				(((pJobQ)->wrIx + (pJobQ)->ringCnt - (pJobQ)->rdIx)%((pJobQ)->ringCnt))
#define	SJOBQ_ISEMPTY(pJobQ)			(((pJobQ)->rdIx) == ((pJobQ)->wrIx))
#define SJOBQ_ISFULL(pJobQ) 			((((pJobQ)->wrIx + SJOBQ_GAP)% (pJobQ)->ringCnt) == (pJobQ)->rdIx)
#define SJOBQ_ISBUSY(pJobQ) 			(SJOBQ_COUNT(pJobQ) >= ((3 * (pJobQ)->ringCnt)/4))

extern	int 	SJobQ_Init(_t_sysJobQ * pJobQ, 
							_t_sysSemB  * pWake, 
							_t_cbentry * jProc, 
							unsigned int ringCnt, 
							unsigned int batch, 
							unsigned int jobCnt, 
							unsigned int jobSz);

extern	int				SJobQ_Sche(_t_sysJobQ * pJobQ, _t_sysJobNode ** nodeA);
extern	int 			SJobQ_NodeFree(_t_sysJobQ * pJobQ, _t_sysJobNode * pJobNode);
extern	_t_sysJobNode * SJobQ_NodeAlloc(_t_sysJobQ * pJobQ);

extern	int 	SJobQ_InitEx(_t_sysJobQ * pJobQ, 
							_t_sysSemB 	* pWake, 
							_t_cbentry 	* jProc,
							unsigned int ringCnt, 
							unsigned int batch, 
							unsigned int jobCnt, 
							unsigned int jobSz);
extern	_t_sysJobNode * SJobQ_NodeAllocEx(_t_sysJobQ * pJobQ, size_t size);
extern	int 			SJobQ_NodeFreeEx(_t_sysJobQ * pJobQ, _t_sysJobNode * pJobNode);
extern	int				SJobQ_ScheEx(_t_sysJobQ * pJobQ, _t_sysJobNode ** nodeA);
extern	int 			SJobQ_Dump(_t_sysJobQ * pJobQ, char * outBuf, int bufLen);


#define SJOBQ_RING_GET(pJobQ, pJobNode)\
do{\
	(pJobNode) = (pJobQ)->ringJob[(pJobQ)->rdIx];	\
	(pJobQ)->rdIx = ((pJobQ)->rdIx + 1)% (pJobQ)->ringCnt;\
}while(0)

#define SJOBQ_RING_PUT(pJobQ, pJobNode, vRet)\
do{\
	vRet = -1;\
	if(!(SJOBQ_ISFULL(pJobQ))){\
		(pJobQ)->ringJob[(pJobQ)->wrIx] = (pJobNode);		\
		(pJobQ)->wrIx = ((pJobQ)->wrIx + 1)% (pJobQ)->ringCnt;\
		vRet = 0;\
	}\
}while(0)

#define SJOBQ_WAKEUP(pJobQ) 	\
do{ \
	SYS_SEM_B_GIVE((pJobQ)->wake);	\
}while(0)

/* When EnQue Fail, Rem to Free the JobNode */
#define SJOBQ_ENQUE(pJobQ, pJobNode, vRet) \
do{ \
	SYS_ENTER_CS(&((pJobQ)->ringLock));		\
	SJOBQ_RING_PUT((pJobQ), (pJobNode), vRet);\
	SYS_LEAVE_CS(&((pJobQ)->ringLock));		\
	SJOBQ_WAKEUP(pJobQ);	\
}while(0)


/* JobQ Command */
extern	int 	SJobQ_SetTraceLvl(int lvl);
extern	int 	SJobQ_GetTraceLvl(void);

#ifdef __cplusplus
}
#endif

#endif

